package com.wj.service.impl;

import com.alibaba.fastjson.JSON;
import com.wj.constant.AuthorityConstant;
import com.wj.constant.CommonConstant;
import com.wj.dao.WjCodeUserDao;
import com.wj.entity.WjCodeUser;
import com.wj.service.IJWTService;
import com.wj.vo.LoginUserInfo;
import com.wj.vo.UsernameAndPassword;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.BASE64Decoder;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class IJWTServiceImpl implements IJWTService {

    private final WjCodeUserDao userDao;

    public IJWTServiceImpl(WjCodeUserDao userDao){
        this.userDao = userDao;
    }

    @Override
    public String generateToken(String username, String password) throws Exception {
        return generateToken(username,password,0);
    }

    @Override
    public String generateToken(String username, String password, int expire) throws Exception {
        //首先需要验证用户是否能够通过授权校验，即输入的用户名和密码能够匹配数据表记录
        WjCodeUser user = userDao.findByUsernameAndPassword(username,password);

        if (Objects.isNull(user)){
            log.error("can not find user: [{}],[{}]",username,password);
            return null;
        }
        //token 中塞入对象，即 jwt 中存储的信息，后端拿到这些信息就可以知道是哪个用户在操作
        LoginUserInfo loginUserInfo = new LoginUserInfo(user.getId(), user.getUsername());

        if (expire <= 0){
            expire = AuthorityConstant.DEFAULT_EXPIRE_DAY;
        }

        //计算超时时间
        ZonedDateTime zdt = LocalDate.now().plus(expire, ChronoUnit.DAYS).atStartOfDay(ZoneId.systemDefault());
        Date expireDate = Date.from(zdt.toInstant());

        return Jwts.builder()
                //jwt payload -> kv
                .claim(CommonConstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserInfo))
                //jwt 的id，用处不大
                .setId(UUID.randomUUID().toString())
                // jwt 过期时间
                .setExpiration(expireDate)
                // jwt 签名 --> 就是用私钥加密
                .signWith(getPrivateKey(), SignatureAlgorithm.RS256)
                .compact();
    }

    @Override
    public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception {
        //校验用户名是否存在，如果存在，不能重复注册
        WjCodeUser oldUser = userDao.findByUsername(usernameAndPassword.getUsername());
        if (Objects.nonNull(oldUser)){
            log.error("username is registered: [{}]",oldUser.getUsername());
            return null;
        }
        WjCodeUser user = new WjCodeUser();
        user.setUsername(usernameAndPassword.getUsername());
        //md5 加密后的密码
        user.setPassword(usernameAndPassword.getPassword());
        user.setExtraInfo("{}");

        //注册一个新用户，写一条记录到数据表里
        user = userDao.save(user);
        log.info("register user success: [{}],[{}]",user.getUsername(),user.getId());
        return generateToken(user.getUsername(),user.getPassword());
    }


    /**
     * 根据本地存储的私钥获取到 PrivateKey 对象
     *
     * @return
     */
    private PrivateKey getPrivateKey() throws Exception {
        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(AuthorityConstant.PRIVATE_KEY));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(priPKCS8);
    }
}
